﻿using Demo.Contract;
using System;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using Thrift.Protocols;
using Thrift.Transports.Client;

namespace Demo.NetCoreClient
{
    class Program
    {
        static void Main(string[] args)
        {
            //var host = "10.65.37.74";
            //var port = 8800;

            //var tcpClient = new TcpClient(host, port);

            //var t = new TSocketClientTransport(tcpClient);
            //var p = new TBinaryProtocol(t);
            //var c = new BPMEngine.Client(p);

            //c.GetDestinationAsync(1, "test", new System.Threading.CancellationToken()).GetAwaiter()
            //    .GetResult();

            //t.Close();

            try
            {
                var config = new PoolConfig()
                {
                    MaxActiveNum = 1,
                    MaxIdleNum = 1,
                    MaxWait = 3000,
                    MinIdleNum = 1
                };
                var poolObject = new PoolObject<Student>(config);
                poolObject.PoolableObjectFactory = new StudentFactory();

                for (var i = 1; i <= 3; i++)
                {
                    var t = new Thread(() =>
                    {
                        try
                        {
                            var s = (Student)poolObject.BorrowObjec();
                            Console.WriteLine($"\t {Thread.CurrentThread.Name}:Borrow success: {s}");

                            Thread.Sleep(1000);

                            poolObject.ReturnObject(s);

                            Console.WriteLine($"\t {Thread.CurrentThread.Name}:Borrow return ok");

                        }
                        catch (Exception e)
                        {
                            Console.WriteLine($"\t ex: {Thread.CurrentThread.Name}" + e.Message);
                        }

                    });
                    t.Name = $"Thread-{i}";
                    t.Start();
                    //Task.Run(() =>
                    //{
                    //   
                    //});
                }


            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }


            Console.ReadLine();

            Console.WriteLine("Done");

        }
    }

    class Student
    {
        public static int Index;

        public int ID { get; set; }

        public override string ToString()
        {
            return $"Student[ID: {ID}]";
        }

    }

    class StudentFactory : IPoolableObjectFactory<Student>
    {
        public Student Create()
        {
            var s = new Student() { ID = ++Student.Index };

            return s;
        }
    }

    public class PoolConfig
    {
        public int MinIdleNum { get; set; }
        public int MaxIdleNum { get; set; }
        public int MaxActiveNum { get; set; }
        public int MaxWait { get; set; }
    }

    public interface IPoolableObjectFactory<T>
    {
        T Create();
    }



    public class PoolObject<T> where T : new()
    {
        public PoolConfig PoolConfig { get; }

        private static ManualResetEvent waiter = new ManualResetEvent(false);
        private static ConcurrentStack<object> _pools = new ConcurrentStack<object>();
        private static volatile int activedCount;

        public IPoolableObjectFactory<T> PoolableObjectFactory { get; set; }

        private static object locker = new object();

        private string CurrentThreadName = Thread.CurrentThread.Name;

        public PoolObject(PoolConfig config)
        {
            this.PoolConfig = config;

        }

        public T BorrowObjec()
        {
            lock (locker)
            {
                Console.WriteLine($"#{Thread.CurrentThread.Name} activedCount: {activedCount}");
                if (_pools.Count == 0)
                {

                    if (activedCount == PoolConfig.MaxActiveNum)
                    {

                        Console.WriteLine($"#{Thread.CurrentThread.Name} 池对象潢了，请等待.");
                        waiter.WaitOne();
                    }
                    else
                    {
                        var o = PoolableObjectFactory.Create();
                        Console.WriteLine($"#{Thread.CurrentThread.Name} 写入对象池: " + o);
                        _pools.Push(o);

                    }
                }

                Object obj;

                if (!_pools.TryPop(out obj)) throw new Exception("不能从池中获取对象.");
                activedCount++;

                if (_pools.Count < PoolConfig.MinIdleNum &&
                        activedCount < PoolConfig.MaxActiveNum)
                {
                    var s = PoolableObjectFactory.Create();
                    Console.WriteLine($"#{Thread.CurrentThread.Name} PoolCount: {_pools.Count}, MinIdle: {PoolConfig.MinIdleNum}, 写入最小空闲对象池: {s} ");
                    _pools.Push(s);
                }

                return (T)obj;
            }



        }

        public void ReturnObject(T instance)
        {
            //if (_pools.Count == PoolConfig.MaxIdleNum)
            //{
            //    ReleaseObject(instance);
            //    return;
            //}
            Console.WriteLine($"#{Thread.CurrentThread.Name} ReturnObject {instance}");
            _pools.Push(instance);
            activedCount--;
            //发通知信号，有对象归还到对象池
            waiter.Set();
        }

        public void ReleaseObject(T instance)
        {
            Console.WriteLine($"[{Thread.CurrentThread.Name}]ReleaseObject");
        }
    }
}
